//@author A0097758E
/**
 * Performs reading of save file and translating the contents into an 
 * ArrayList of Task objects. Also writes an ArrayList of Task objects 
 * into a text file for persistence.
 * 
 * Here is a sample of the template.
 * See nus-cs2103t-quick-todo/Sample_Text_Files/quick-todo.txt-[TEMPLATEwithSAMPLE].txt
 *
 * FIELD NAME										EXAMPLE
 * NEW_LINE_DELIMITER								...too long...
 * TASK ID											1
 * TITLE											Math Homework
 * DESCRIPTION										Do math homework
 * PRIORITY											1
 * START_DATE_DD;START_DATE_MM;START_DATE_YYYY		03;03;1999
 * START_TIME_IN_24_HOUR_FORMAT						1234
 * START_DATE_VALID;START_TIME_VALID				true;true
 * END_DATE_DD;END_DATE_MM;END_DATE_YYYY			04;04;1999
 * END_TIME_IN_24_HOUR_FORMAT						2345
 * END_DATE_VALID;END_TIME_VALID					true;true
 */

package storage;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

import sharedUtils.Constants;

public class FileHandler {
	private static final String DATE_TIME_DELIMITER_REGEX = "\\;";
	private static final String DATE_TIME_DELIMITER_STRING = ";";
	private static final String NEW_LINE_DELIMITER = 
			"504a0ef099e725faebd9428a4c100404c3e5f29c8257c986440410e68de17050e0114c355b92b8275033373777311855";
	
	private static boolean DEBUG = Constants.isDebugModeEnabled;
	
	private String fileName;
	private boolean isInit;
	
	private File f;
	
	/**
	 * Default Constructor. Do not use - Use FileHandler(String fileLoc) 
	 * instead. Instantiate this object, referencing the file defined in 
	 * Constants.DEFAULT_FILE_LOC.
	 */
	public FileHandler() {
		this(Constants.DEFAULT_FILE_LOC);
	}
	
	/**
	 * Instantiate this object, interpreting the given String as the 
	 * relative filepath to the save file.
	 * 
	 * @param fileLoc Location of file to perform read/write operations on
	 * @throws IllegalArgumentException Given fileLoc is null.
	 */
	public FileHandler(String fileLoc) throws IllegalArgumentException {
		if(Constants.areAssertsEnabled) {
			/**
			 * This assertion is to fulfill the requirement that 
			 * we have applied assertions into the source code.
			 */
			assert(fileLoc != null);
		}
		
		if(fileLoc == null) {
			throw new IllegalArgumentException(Constants.INVALID_NULL_ARG_MSG);
		}
		
		fileName = fileLoc;
		
		try { // to create file object
			f = new File(fileLoc);
			
			if (!f.exists()) {
				f.createNewFile();
			}
			
			isInit = true;
		} catch (IOException ioe) {
			System.err.println("[EXCEPTION, FileHandler] Constructor: IOException encountered.");
			ioe.printStackTrace();
			System.out.println();
			
			isInit = false;
		} //end of try/catch
	}
	
	/**
	 * Returns true if this object was successfully initialized. False otherwise.
	 * 
	 * @return True if this object was successfully initialized. False otherwise.
	 */
	public boolean isInit() {
		return isInit;
	}
	
	/**
	 * Returns the filepath given to this object's constructor during initialization.
	 * 
	 * @return The filepath given to this object's constructor during initialization.
	 */
	public String getFileName() {
		return fileName;
	}
	
	/**
	 * Check if the given String consists of all integers.
	 * 
	 * @param s String to be checked
	 * @return Returns one of the following error codes: 
	 * Constants.SUCCESS_ERROR_CODE, 
	 * Constants.INVALID_NULL_ARG or 
	 * Constants.INVALID_NOT_POSITIVE_INT_ARG
	 */
	private int checkPositiveInt(String s) {
		/**
		 * Check arguments
		 */
		if(s == null) {
			return Constants.INVALID_NULL_ARG;
		}
		
		/**
		 * Logic
		 */
		if(!s.matches(Constants.POSITIVE_INTEGER_REGEX)) {
			return Constants.INVALID_NOT_POSITIVE_INT_ARG;
		}
		
		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Encodes the ArrayList of Task objects and write them into the file 
	 * associated with this FileHandler.
	 * 
	 * @param al ArrayList of Task objects to be written.
	 * @return Returns one of the following error codes: 
	 * Constants.SUCCESS_ERROR_CODE, 
	 * Constants.FILEHANDLER_FAILED_WRITE_FILE_DOES_NOT_EXIST, 
	 * Constants.INVALID_NULL_ARG or 
	 * Constants.IOEXCEPTION_TRIGGERED.
	 */
	public int write(ArrayList<Task> al) {
		if(!isInit) {
			return Constants.FILEHANDLER_FAILED_WRITE_FILE_DOES_NOT_EXIST; 
		}
		
		return encodeToFile(al);
	}
	
	/**
	 * Encodes the ArrayList of Task objects and write them into the file 
	 * associated with this FileHandler.
	 * 
	 * @param al ArrayList of Task objects to be written.
	 * @return Returns one of the following error codes: 
	 * Constants.SUCCESS_ERROR_CODE,
	 * Constants.INVALID_NULL_ARG or 
	 * Constants.IOEXCEPTION_TRIGGERED.
	 */
	private int encodeToFile(ArrayList<Task> al) {
		int errorCode = Constants.SUCCESS_ERROR_CODE;
		
		if(al == null) {
			return Constants.INVALID_NULL_ARG;
		}
		
		FileWriter fw = null;
		BufferedWriter bw = null;
		
		try{ // to create bw object
			fw = new FileWriter(f.getAbsoluteFile()); // overwrite file
			bw = new BufferedWriter(fw);
			
			/**
			 * This is the part that actually reads the content and writes to the file
			 */			
			for(int i = 0; i < al.size(); i++) {
				encodeTask(bw, al.get(i));
			}			
		} catch(IOException ioe) {
			if (DEBUG) {
				System.err.println("[EXCEPTION, FileHandler] encodeToFile(): IOException encountered while writing.");
				ioe.printStackTrace();
				System.out.println();
			}
			
			errorCode = Constants.IOEXCEPTION_TRIGGERED;
		}
		
		try{
			if(bw != null) {
				bw.close();
			}
			
			if(fw != null) {
				fw.close();
			}
		} catch(Exception e) {
			System.err.println("[EXCEPTION, FileHandler] encodeToFile(): Exception encountered when trying to close BW/FW.");
			e.printStackTrace();
			System.out.println();
		}
		
		if(errorCode == Constants.SUCCESS_ERROR_CODE) {
			return Constants.SUCCESS_ERROR_CODE;
		} else {
			return errorCode;
		}
	}
	
	/**
	 * Checks if the given Task is valid. This helps prevent invalid Tasks 
	 * from being saved and propagating potential errors.
	 * 
	 * @param t Task object to be checked.
	 * @return True if the Task is valid. False otherwise.
	 */
	private boolean checkIfValidForWrite(Task t) {
		if(t.getTitle() == null || t.getId() < 0 || t.getPriority() < 0) {
			return false;
		}
		
		return true;
	}
	
	/**
	 * Encodes the one Task object, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param t Task to be written.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeTask(BufferedWriter bw, Task t) throws IOException {
		if(!checkIfValidForWrite(t)) {
			return;
		}
		
		encodeDelimiter(bw, NEW_LINE_DELIMITER);
		
		encodeTaskId(bw, t.getId());
		
		encodeTitle(bw, t.getTitle());
		encodeDescription(bw, t.getDescription());
		
		encodePriority(bw, t.getPriority());
		
		encodeSDate(bw, t.getStartDate());
		encodeSDateBools(bw, t.isStartDateSet(), t.isStartTimeSet());
		encodeEDate(bw, t.getEndDate());
		encodeEDateBools(bw, t.isEndDateSet(), t.isEndTimeSet());
	}
	
	/**
	 * Encodes the Task delimiter, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param delimiter String used to delimit Tasks within the file.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeDelimiter(BufferedWriter bw,
			String delimiter) throws IOException {
		encodeString(bw, delimiter);
	}

	/**
	 * Encodes the TaskId, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param id TaskId.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeTaskId(BufferedWriter bw,
			int id) throws IOException {
		encodeString(bw, Integer.toString(id));
	}

	/**
	 * Encodes the Task title, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param title Task title.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeTitle(BufferedWriter bw,
			String title) throws IOException {
		encodeString(bw, title);
	}

	/**
	 * Encodes the Task description, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param description Task description.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeDescription(BufferedWriter bw,
			String description) throws IOException {
		encodeString(bw, description);		
	}

	/**
	 * Encodes the Task priority, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param priority Task priority.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodePriority(BufferedWriter bw,
			int priority) throws IOException {
		encodeString(bw, Integer.toString(priority));
	}

	/**
	 * Encodes the Task start date, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param startDate Task GregorianCalendar start date.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeSDate(BufferedWriter bw, 
			GregorianCalendar startDate) throws IOException {
		encodeGregCal(bw, startDate);
	}

	/**
	 * Encodes the Task start date booleans, isStartDateSet and isStartTimeSet, 
	 * writing it into the file associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param startDateSet Task isStartDateSet
	 * @param startTimeSet Task isStartTimeSet
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeSDateBools(BufferedWriter bw, boolean startDateSet,
			boolean startTimeSet) throws IOException {
		encodeString(bw, Boolean.toString(startDateSet) + 
				DATE_TIME_DELIMITER_STRING + Boolean.toString(startTimeSet));
	}

	/**
	 * Encodes the Task end date, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param endDate Task GregorianCalendar end date.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeEDate(BufferedWriter bw, 
			GregorianCalendar endDate) throws IOException {
		encodeGregCal(bw, endDate);
	}
	
	/**
	 * Encodes the Task end date booleans, isEndDateSet and isEndTimeSet, 
	 * writing it into the file associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param endDateSet Task isEndDateSet
	 * @param endTimeSet Task isEndTimeSet
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeEDateBools(BufferedWriter bw, boolean endDateSet,
			boolean endTimeSet) throws IOException {
		encodeString(bw, Boolean.toString(endDateSet) + 
				DATE_TIME_DELIMITER_STRING + Boolean.toString(endTimeSet));
	}
	
	/**
	 * Encodes the given String, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param str String to encode and write.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeString(BufferedWriter bw, String str) throws IOException {
		if(str == null) {
			str = "";
		}
		
		bw.write(str);
		bw.newLine();
	}
	
	/**
	 * Encodes the given GregorianCalendar date, writing it into the file 
	 * associated with this FileHandler.
	 * 
	 * @param bw BufferedWriter created that references the file to write into.
	 * @param tempCal GregorianCalendar to encode and write.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedWriter;
	 */
	private void encodeGregCal(BufferedWriter bw,
			GregorianCalendar tempCal)throws IOException {
		String str1, str2;
		
		if(tempCal == null) {
			str1 = "";
			str2 = "";
		} else {
			str1 = String.format("%02d", (tempCal.get(Calendar.DAY_OF_MONTH) + 1)) + 
				DATE_TIME_DELIMITER_STRING + String.format("%02d", (tempCal.get(Calendar.MONTH) + 1)) + 
				DATE_TIME_DELIMITER_STRING + String.format("%04d", tempCal.get(Calendar.YEAR));
			
			str2 = String.format("%02d", tempCal.get(Calendar.HOUR_OF_DAY))
					+ String.format("%02d", tempCal.get(Calendar.MINUTE));
		}
		
		encodeString(bw, str1);
		encodeString(bw, str2);
	}

	/**
	 * The following function loops through the entire file and writes each line
	 * into a ArrayList of Tasks, with each line being an element in the ArrayList,
	 * and returns it to the calling function.
	 * 
	 * If it fails to, returns an empty ArrayList.
	 */
	
	/**
	 * Reads, extracts and decodes the file associated with this FileHandler, 
	 * creating an ArrayList of Task objects based on the save file contents.
	 * 
	 * @return An ArrayList of Task objects extracted from the save file. 
	 * Returns an empty ArrayList of Task objects if this FileHandler 
	 * object did not successfully initialize. Returns null on other errors.
	 */
	public ArrayList<Task> read() {
		if (!isInit) {
			return new ArrayList<Task>();
		}
		
		return decodeThisFile();
	}
	
	/**
	 * Reads, extracts and decodes the file associated with this FileHandler, 
	 * creating an ArrayList of Task objects based on the save file contents.
	 * 
	 * @return An ArrayList of Task objects extracted from the save file. 
	 * Returns null on errors.
	 */
	private ArrayList<Task> decodeThisFile() {
		ArrayList<Task> result = new ArrayList<Task>();
		Task tempTask;
		
		BufferedReader br = null;
		
		try { // to create br object
			br = new BufferedReader(new FileReader(f));
			
			while(readUntilNewLineDelimiter(br)) {
				tempTask = decodeTask(br);
				
				if(tempTask != null) {
					result.add(tempTask);
				}
			}
		} catch(IOException ioe) {
			if (DEBUG) {
				System.err.println("[EXCEPTION, FileHandler] decodeThisFile(): IOException encountered while reading.");
				ioe.printStackTrace();
				System.out.println();
			} 
			
			return null;
		}
		
		try{
			if(br != null) {
				br.close();
			}
		} catch(Exception e) {
			System.err.println("[EXCEPTION, FileHandler] decodeThisFile(): Exception encountered when trying to close BR.");
			e.printStackTrace();
			System.out.println();
		}
		
		return result;
	}
	
	/*
	 * Here's the format again, in case you forget and are lazy to scroll up
	 * 
	 * FIELD NAME										EXAMPLE
	 * NEW_LINE_DELIMITER								...too long...
	 * TASK ID											1
	 * TITLE											Math Homework
	 * DESCRIPTION										Do math homework
	 * PRIORITY											1
	 * START_DATE_DD;START_DATE_MM;START_DATE_YYYY		03;03;1999
	 * START_TIME_IN_24_HOUR_FORMAT						1234
	 * START_DATE_VALID;START_TIME_VALID				true;true
	 * END_DATE_DD;END_DATE_MM;END_DATE_YYYY			04;04;1999
	 * END_TIME_IN_24_HOUR_FORMAT						2345
	 * END_DATE_VALID;END_TIME_VALID					true;true
	 */
	/**
	 * Reads, extracts and decodes 1x Task object from the file, associated 
	 * with this FileHandler, from the current position of cursor in the 
	 * given BufferedReader.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return 1 Task object. Returns null on error.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private Task decodeTask(BufferedReader br) throws IOException {
		// Task ID
		int tempTaskId = decodeTaskId(br);
		if(tempTaskId < 0) {
			return null;
		}
		
		// Title
		String tempTitle = decodeTitle(br);
		if(tempTitle == null || tempTitle.length() <= 0) {
			return null;
		}
		
		// Description
		String tempDesc = decodeDescription(br);
		if(tempDesc == null) {
			return null;
		} else if(tempDesc.length() <= 0) {
			tempDesc = null;
		}
		
		// Priority
		int tempPriority = decodePriority(br);
		if(tempPriority < 0) {
			return null;
		}
		
		GregorianCalendar tempSDate = decodeSDate(br);
		
		boolean[] startValid = decodeStartValid(br);
		if(startValid.length != 2) {
			startValid = new boolean[] {false, false};
		}
				
		GregorianCalendar tempEDate = decodeEDate(br);
		
		boolean[] endValid = decodeEndValid(br);
		if(endValid.length != 2) {
			endValid = new boolean[] {false, false};
		}
		
		return new Task(tempSDate, tempEDate,
				startValid[0], startValid[1],
				endValid[0], endValid[1],
				tempTitle, tempDesc, 
				tempPriority, tempTaskId);
	}

	/**
	 * Reads until the first occurrence of FileHandler.NEW_LINE_DELIMITER 
	 * from the current position of cursor in the given BufferedReader.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return True when FileHandler.NEW_LINE_DELIMITER has been read away. 
	 * False when EOF is reached.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private boolean readUntilNewLineDelimiter(BufferedReader br) throws IOException {
		if(br == null) {
			throw new IOException();
		}
		
		String line = br.readLine();
		
		while(line != null) {
			if(line.equalsIgnoreCase(NEW_LINE_DELIMITER)) {
				return true;
			}
			
			line = br.readLine();
		}
		
		return false;
	}
	
	/**
	 * Reads a line and decode it as the TaskId.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task title as an int.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private int decodeTaskId(BufferedReader br) throws IOException {
		return decodeInt(br);
	}
	
	/**
	 * Reads a line and decode it as the Task title.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task title as a String.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private String decodeTitle(BufferedReader br) throws IOException {
		return decodeString(br);
	}
	
	/**
	 * Reads a line and decode it as the Task description.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task description as a String.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private String decodeDescription(BufferedReader br) throws IOException {
		return decodeString(br);
	}
	
	/**
	 * Reads a line and decode it as the Task priority.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task priority as an int.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private int decodePriority(BufferedReader br) throws IOException {
		return decodeInt(br);
	}
	
	/**
	 * Reads two lines (date and time) and decode them as the Task start date.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task startDate as a GregorianCalendar.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private GregorianCalendar decodeSDate(BufferedReader br) throws IOException {
		return decodeForStartEndDate(br);
	}
	
	/**
	 * Reads a line and decode it as the Task startDate booleans, 
	 * isStartDateSet and isStartTimeSet.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task isStartDateSet and isStartTimeSet as a boolean array in that order.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private boolean[] decodeStartValid(BufferedReader br) throws IOException {
		return decodeStartEndValid(br);
	}
	
	/**
	 * Reads two lines (date and time) and decode them as the Task end date.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task endDate as a GregorianCalendar.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private GregorianCalendar decodeEDate(BufferedReader br) throws IOException {
		return decodeForStartEndDate(br);
	}
	
	/**
	 * Reads a line and decode it as the Task endDate booleans, 
	 * isEndDateSet and isEndTimeSet.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Task isSEndDateSet and isEndTimeSet as a boolean array in that order.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private boolean[] decodeEndValid(BufferedReader br) throws IOException {
		return decodeStartEndValid(br);
	}
	
	/**
	 * Reads a line and decode it as a positive integer, translating it into an int.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Line translated into an int. If line is not a positive integer, 
	 * return one of the following error codes: 
	 * Constants.INVALID_NULL_ARG or 
	 * Constants.INVALID_NOT_POSITIVE_INT_ARG.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private int decodeInt(BufferedReader br) throws IOException {
		if(br == null) {
			throw new IOException();
		}
		
		String line = br.readLine();
		
		if(line == null) {
			return Constants.INVALID_NULL_ARG;
		}
		
		int errorCode = checkPositiveInt(line);
		
		if(errorCode == 0) {
			return Integer.parseInt(line);
		} else if(DEBUG) {			
			System.err.println("[ERROR, FileHandler] readForInt(): " + line + " is not a valid integer.");
		}
		
		return errorCode;
	}
	
	/**
	 * Reads a line and decode it as a String.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return String. May return null if EOF encountered.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private String decodeString(BufferedReader br) throws IOException {
		if(br == null) {
			throw new IOException();
		} else {
			return br.readLine();
		}
	}

	/**
	 * Reads two lines (date and time) and decode them as a GregorianCalendar
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return GregorianCalendar object. Returns null on error.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private GregorianCalendar decodeForStartEndDate(BufferedReader br) throws IOException {
		if(br == null) {
			throw new IOException();
		}
		
		/**
		 * Logic
		 * Read for Date
		 */
		String line = br.readLine();
		String[] date;
		int hour, min, year, month, day;
		
		if(line == null) {
			return null;
		} else if(line.length() > 0) {
			date = line.split(DATE_TIME_DELIMITER_REGEX);
			
			if(date.length != 3 ||
					checkPositiveInt(date[0]) != 0 || 
					checkPositiveInt(date[1]) != 0 ||
					checkPositiveInt(date[2]) != 0 ||
					date[0].length() != 2 ||
					date[1].length() != 2 ||
					date[2].length() != 4) {
				/**
				 * Invalid date detected
				 * This next line will be the time associated to this date
				 * Regardless if it has a value or not, disregard it
				 */
				br.readLine();
				return null;
			}
			
			day = Integer.parseInt(date[0]);
			month = Integer.parseInt(date[1]);
			year = Integer.parseInt(date[2]);
			
			/**
			 * TODO: Make the check for days month-specific
			 * i.e. Feb only has <= 28 days
			 */
			if(day > 31 || month > 12 || year < 1970) {
				/**
				 * Invalid date detected
				 * This next line will be the time associated to this date
				 * Regardless if it has a value or not, disregard it
				 */
				br.readLine();
				return null;
			}
		} else {
			/**
			 * Blank date detected
			 * Null startDate/endDate was written
			 * This next line will be the time associated to this date
			 * Regardless if it has a value or not, disregard it
			 */
			br.readLine();
			return null;
		}
		
		/**
		 * Read for Time
		 */
		line = br.readLine();
		
		if(line == null || 
				line.length() != 4 || 
				!line.matches(Constants.POSITIVE_INTEGER_REGEX)) {
			return null;
		}
		
		hour = Integer.parseInt(line.substring(0, 2));
		min = Integer.parseInt(line.substring(2, 4));
		
		if(hour > 24 || min > 59) {
			return null;
		}
		
		return new GregorianCalendar(year, month-1, day-1, hour, min);
	}
	
	/**
	 * Reads a line and decode it as a 1d boolean array of size 2.
	 * 
	 * @param br BufferedReader created that references the file to write into.
	 * @return Boolean array of size 2. Returns null on error.
	 * @throws IOException If an I/O error occurs as given by java.io.BufferedReader;
	 */
	private boolean[] decodeStartEndValid(BufferedReader br) throws IOException {
		if(br == null) {
			throw new IOException();
		}
		
		String line = br.readLine();
		String[] isValidStrArr;
		boolean[] isValidBoolArr = new boolean[] {false, false};
		
		if(line == null || line.length() <= 0) {
			return isValidBoolArr;
		}
		
		isValidStrArr = line.split(DATE_TIME_DELIMITER_REGEX);
		
		if(isValidStrArr.length != 2) {
			return isValidBoolArr;
		}
		
		if(isValidStrArr[0] != null && 
				isValidStrArr[0].equalsIgnoreCase("true")) {
			isValidBoolArr[0] = true;
		} else {
			isValidBoolArr[0] = false;
		}
		
		if(isValidStrArr[1] != null && 
				isValidStrArr[1].equalsIgnoreCase("true")) {
			isValidBoolArr[1] = true;
		} else {
			isValidBoolArr[1] = false;
		}
				
		return isValidBoolArr;
		
	}
	
	
}
